﻿//
// Copyright (c) 2009-2010 Construction Bid online.  All rights reserved.
// Use, duplication, or modification subject to license, consulting, and/or employment agreement(s).
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;


namespace LRSTaxServices
{
    public class MailUtils
    {
        #region Types

        [Flags]
        public enum EmailValidationCheck : int
        {
            None                = 0,
            Syntax              = 1,
            ReverseDNS          = 2,
            MXRecords           = 4,
            SMTP_VRFY_RCPTTO    = 8
        }

        #endregion

        #region Construction / Disposal

        public MailUtils() { }

        #endregion

        #region IsValidEmailAddress

        /// <summary>
        /// Returns true iff the specified 'emailAddr' is a syntactically correct email address.
        /// </summary>
        public static bool IsValidEmailAddress(string emailAddr)
        {
            if (String.IsNullOrEmpty(emailAddr))
                return (false);

            string strRegex = @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}" +
                             @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
                             @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";

            Regex re = new Regex(strRegex);

            return (re.IsMatch(emailAddr));
        }

        #endregion

        #region SendEmail

        public static bool SendEmail(string toEmailAddr, string toDisplayName, string subject, string bodyText, out string errMsg)
        {
            return (MailUtils.SendEmail(toEmailAddr, toDisplayName, null, null, subject, bodyText, out errMsg));
        }


        public static bool SendEmail(string toEmailAddr, string toDisplayName, string fromEmailAddr,
                                    string fromDisplayName, string subject, string bodyText, out string errMsg)
        {
            errMsg = "";

            SmtpClient smtpClient = new SmtpClient();
            System.Net.NetworkCredential SMTPUserInfo = new System.Net.NetworkCredential("admin@constructionbidonline.com", "gtus2736");
            smtpClient.UseDefaultCredentials = false;
            smtpClient.Credentials = SMTPUserInfo;

            MailMessage message = new MailMessage();

           

            if (String.IsNullOrEmpty(toEmailAddr))
            {
                errMsg = "The 'toEmailAddr' parameter may neither be Null nor Empty.";
                return (false);
            }
            else if ((toDisplayName == null) || (toDisplayName.Length == 0))
            {
                toDisplayName = toEmailAddr;
            }

			if (String.IsNullOrEmpty(fromEmailAddr))
            {
                // TODO: Fetch these from Web.config
                string fromAddr = (ConfigurationManager.AppSettings["DefaultEmailFromAddress"] != null) ? ConfigurationManager.AppSettings["DefaultEmailFromAddress"] : "admin@constructionbidonline.com";
                fromEmailAddr = fromAddr;

                string fromAddrDisplay = (ConfigurationManager.AppSettings["DefaultEmailFromAddressDisplayName"] != null) ? ConfigurationManager.AppSettings["DefaultEmailFromAddressDisplayName"] : "admin@constructionbidonline.com";
                fromDisplayName = fromAddrDisplay;
            }

            try
            {
                // Specify the host name or ipaddress of your server
                // Default in IIS will be localhost.
                string smtpHostAddr = (ConfigurationManager.AppSettings["SmtpHost"] != null) ? ConfigurationManager.AppSettings["SmtpHost"] : "mail.constructionbidonline.com";
                smtpClient.Host = smtpHostAddr;

                //Default port will be 25
                smtpClient.Port = 25;

                //From address will be given as a MailAddress Object

                MailAddress fromAddress = new MailAddress(fromEmailAddr, fromDisplayName);
                message.From = fromAddress;

                // To address collection of MailAddress
                MailAddress toAddress = new MailAddress(toEmailAddr, toDisplayName);
                message.To.Add(toAddress);

                message.Subject = subject;

                // CC and BCC optional
                //message.CC.Add("admin1@yoursite.com");
                //message.Bcc.Add("admin2@yoursite.com");

                //Body can be Html or text format
                //Specify true if it  is html message
                message.IsBodyHtml = true;

                // Message body content
                message.Body = bodyText;

                // Send SMTP mail
                smtpClient.Send(message);
            }
            catch (Exception ex)
            {
                errMsg = ex.Message + (ex.InnerException != null ? " " + ex.InnerException.Message : "");
                return (false);
            }

            return (true);
        }

        #endregion
    }


    public class NetworkUtils
    {
        #region GetDnsServers

        /// <summary>
        /// Returns all DNS addresses from all operational interfaces on the localhost (IPv4 and/or IPv6).
        /// </summary>
        public static List<IPAddress> GetDnsServers()
        {
            List<IPAddress> dnsServerIPs = new List<IPAddress>(4);
            NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
            foreach (NetworkInterface nic in nics)
            {
                if (nic.OperationalStatus == OperationalStatus.Up)
                {
                    IPAddressCollection IPs = nic.GetIPProperties().DnsAddresses;
                    foreach (IPAddress ip in IPs)
                        dnsServerIPs.Add(ip);
                }
            }
            return (dnsServerIPs);
        }

        #endregion

        #region GetMxRecords

        public static List<MXRecord> GetMxRecords(string host)
        {
            List<IPAddress> dnsServerIPs = GetDnsServers();

            ArrayList dnsServerAddrs = new ArrayList(4);
            foreach (IPAddress ipAddr in dnsServerIPs)
                if (ipAddr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    dnsServerAddrs.Add(ipAddr.ToString());

            DnsLite dnsLite = new DnsLite();
            dnsLite.SetDnsServers(dnsServerAddrs);

            List<MXRecord> results = dnsLite.GetMXRecords(host);

            return (results);
        }

        #endregion
    }

    #region DNSLite

    public class MXRecord
    {
        public int preference = -1;
        public string exchange = null;

        public override string ToString()
        {
            return "Preference : " + preference + " Exchange : " + exchange;
        }
    }


    internal class DnsLite
    {
        private byte[] data;
        private int position, id, length;
        private string name;
        private ArrayList dnsServers;

        private static int DNS_PORT = 53;
        Encoding ASCII = Encoding.ASCII;

        public DnsLite()
        {
            id = DateTime.Now.Millisecond * 60;
            dnsServers = new ArrayList();
        }


        public void SetDnsServers(ArrayList dnsServers)
        {
            this.dnsServers = dnsServers;
        }


        public List<MXRecord> GetMXRecords(string host)
        {
            List<MXRecord> mxRecords = null;
            for (int i = 0; i < dnsServers.Count; i++)
            {
                try
                {
                    mxRecords = GetMXRecords(host, (string)dnsServers[i]);
                    break;
                }
                catch (IOException)
                {
                    continue;
                }
            }

            return (mxRecords);
        }


        private int getNewId()
        {
            //return a new id
            return ++id;
        }


        public List<MXRecord> GetMXRecords(string host, string serverAddress)
        {
            // Opening the UDP socket at DNS server
            // use UDPClient, if you are still with Beta1
            UdpClient dnsClient = new UdpClient(serverAddress, DNS_PORT);

            // Preparing the DNS query packet.
            makeQuery(getNewId(), host);

            // Send the data packet
            dnsClient.Send(data, data.Length);

            IPEndPoint endpoint = null;
            // Receive the data packet from DNS server
            data = dnsClient.Receive(ref endpoint);

            length = data.Length;

            // Un-pack the byte array & make an array of MXRecord objects.
            return (makeResponse());
        }


        // Pack query information in format expected by server.
        public void makeQuery(int id, String name)
        {
            data = new byte[512];

            for (int i = 0; i < 512; ++i)
            {
                data[i] = 0;
            }

            data[0] = (byte)(id >> 8);
            data[1] = (byte)(id & 0xFF);
            data[2] = (byte)1; data[3] = (byte)0;
            data[4] = (byte)0; data[5] = (byte)1;
            data[6] = (byte)0; data[7] = (byte)0;
            data[8] = (byte)0; data[9] = (byte)0;
            data[10] = (byte)0; data[11] = (byte)0;

            string[] tokens = name.Split(new char[] { '.' });
            string label;

            position = 12;

            for (int j = 0; j < tokens.Length; j++)
            {

                label = tokens[j];
                data[position++] = (byte)(label.Length & 0xFF);
                byte[] b = ASCII.GetBytes(label);

                for (int k = 0; k < b.Length; k++)
                {
                    data[position++] = b[k];
                }
            }

            data[position++] = (byte)0; data[position++] = (byte)0;
            data[position++] = (byte)15; data[position++] = (byte)0;
            data[position++] = (byte)1;
        }


        // Un-pack the byte array
        public List<MXRecord> makeResponse()
        {
            List<MXRecord> mxRecords = new List<MXRecord>();
            MXRecord mxRecord;

            //NOTE: we are ignoring the unnecessary fields.
            //		and takes only the data required to build
            //		MX records.

            int qCount = ((data[4] & 0xFF) << 8) | (data[5] & 0xFF);
            if (qCount < 0)
            {
                throw new IOException("invalid question count");
            }

            int aCount = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);
            if (aCount < 0)
            {
                throw new IOException("invalid answer count");
            }

            position = 12;

            for (int i = 0; i < qCount; ++i)
            {
                name = "";
                position = proc(position);
                position += 4;
            }

            for (int i = 0; i < aCount; ++i)
            {
                name = "";
                position = proc(position);

                position += 10;

                int pref = (data[position++] << 8) | (data[position++] & 0xFF);

                name = "";
                position = proc(position);

                mxRecord = new MXRecord();

                mxRecord.preference = pref;
                mxRecord.exchange = name;

                mxRecords.Add(mxRecord);
            }

            return (mxRecords);
        }


        private int proc(int position)
        {
            int len = (data[position++] & 0xFF);

            if (len == 0)
                return (position);

            int offset;
            do
            {
                if ((len & 0xC0) == 0xC0)
                {
                    if (position >= length)
                        return (-1);

                    offset = ((len & 0x3F) << 8) | (data[position++] & 0xFF);
                    proc(offset);
                    return position;
                }
                else
                {
                    if ((position + len) > length)
                        return (-1);

                    name += ASCII.GetString(data, position, len);
                    position += len;
                }

                if (position > length)
                    return (-1);

                len = data[position++] & 0xFF;

                if (len != 0)
                    name += ".";

            } while (len != 0);

            return (position);
        }
    }

    #endregion
}